#include "utilities.h"

void storeProcessing(msg_header msgRespHdr, listBuffStruct eventReq)
{
	map<int,writeListData>::iterator it;
	list<fromUOIDStr>::iterator itF;
	//Process the Store request accordingly
	if((uint8_t)msgRespHdr.msgtype == STOR)
	{
		if(printFlag == 1)
		{
			cout<<"Store Command Recieved from "<<eventReq.portNumber<<endl;
		}
		int totalDataLength = msgRespHdr.datalength;
		int forCounter = totalDataLength/8192;
		int extraCounter = totalDataLength%8192;
		listBuffStruct tempListBuff = eventQueue.front();
		uint32_t metaLength;
		memcpy(&metaLength,&(tempListBuff.listBuff[0]),sizeof(uint32_t));
		int f = 0;
		int testUOID = findInUOIDList(msgRespHdr.uoid);
		pthread_mutex_lock(&cacheSizeLock);
		if(testUOID == 1)
		{
			pthread_mutex_unlock(&cacheSizeLock);
			//msg is duplicate
			if(printFlag == 1)
			{
				cout<<"Duplicate Problem"<<endl;
			}
			pthread_mutex_lock(&eventQueueMutex);
			eventQueue.pop_front();
			for(f=1;f<forCounter;f++)
			{
				eventQueue.pop_front();
			}
			if(extraCounter!=0)
			{
				eventQueue.pop_front();
			}	
			pthread_mutex_unlock(&eventQueueMutex);
		}
		else if(drand48()>initMap["StoreProb"].asDouble)
		{
			pthread_mutex_unlock(&cacheSizeLock);
			if(printFlag == 1)
			{
				cout<<"Probability Problem"<<endl;
			}
			//cache probability
			pthread_mutex_lock(&eventQueueMutex);
			eventQueue.pop_front();
			for(f=1;f<forCounter;f++)
			{
				eventQueue.pop_front();
			}
			if(extraCounter!=0)
			{
				eventQueue.pop_front();
			}	
			pthread_mutex_unlock(&eventQueueMutex);
		}
		else
		{
			if(totalDataLength > initMap["CacheSize"].asDouble*1000)
			{
				if(printFlag == 1)
				{
					cout<<"File too big to be stored in this cache"<<endl;
				}
				pthread_mutex_unlock(&cacheSizeLock);
				//File too big
				pthread_mutex_lock(&eventQueueMutex);
				eventQueue.pop_front();
				for(f=1;f<forCounter;f++)
				{
					eventQueue.pop_front();
				}
				if(extraCounter!=0)
				{
					eventQueue.pop_front();
				}	
				pthread_mutex_unlock(&eventQueueMutex);
				return;
			}
			while(currentCacheSize + totalDataLength - metaLength > initMap["CacheSize"].asDouble*1000)
			{
				//cache size is exceeding
				if(printFlag == 1)
				{
					cout<<"Cache Problem Current Size"<<currentCacheSize<<endl;
				}
				pthread_mutex_lock(&LRULock);
				int indexRemoved = LRUList.front();
				LRUList.pop_front();
				pthread_mutex_unlock(&LRULock);
				list<storeListStr>::iterator itF;
				char storefilename[512];
				char filename[512];
				FILE *pFile;
				pthread_mutex_lock(&storelock);
				for(itF=myStoreList.begin(); itF!=myStoreList.end(); itF++)
				{
					storeListStr storeTemp = *itF;
					if(storeTemp.fileIndex==indexRemoved)
					{
						int index = storeTemp.fileIndex;
						if(printFlag == 1)
						{
							cout<<"Cached File Found"<<endl;
							cout<<index<<endl;
						}
						char fc[10];
						memset(fc,'\0',10);
						sprintf(fc,"%d",index);
						if(printFlag == 1)
						{
							cout<<"Match Found Deleting File"<<endl;
						}
						strcpy(storefilename,initMap["HomeDir"].asStr);
						strcat(storefilename,"/");
						strcat(storefilename,"files");
						strcat(storefilename,"/");
						strcat(storefilename,fc);
						char deletefilename[8192];
						memset(deletefilename,'\0',8192);
						strcpy(deletefilename,storefilename);
						strcat(deletefilename,".data");
						if( remove(deletefilename) != 0 )
						perror( "Error deleting file" );
						else
						{
						if(printFlag == 1)
						{
							printf("%s.data File successfully deleted\n",fc );
						}
						}
						memset(deletefilename,'\0',8192);
						strcpy(deletefilename,storefilename);
						strcat(deletefilename,".meta");
						if( remove(deletefilename) != 0 )
						{
							perror( "Error deleting file" );
						}
						else
						{
							if(printFlag == 1)
							{
								printf("%s.meta File successfully deleted\n",fc );
							}
						}
						currentCacheSize = currentCacheSize - storeTemp.fileSize;
						if(printFlag == 1)
						{
							cout<<myStoreList.size()<<endl;
						}
						itF = myStoreList.erase(itF);
						if(printFlag == 1)
						{
							cout<<myStoreList.size()<<endl;
						}
					}							
				}
				
				strcpy(storefilename,initMap["HomeDir"].asStr);
				strcat(storefilename,"/");
				strcpy(filename,storefilename);
				strcat(filename,"kwrd_index");
				pFile=fopen(filename,"w");
				fclose(pFile);
				strcpy(filename,storefilename);
				strcat(filename,"sha1_index");
				pFile=fopen(filename,"w");
				fclose(pFile);
				strcpy(filename,storefilename);
				strcat(filename,"nonce_index");
				pFile=fopen(filename,"w");
				fclose(pFile);
				strcpy(filename,storefilename);
				strcat(filename,"name_index");
				pFile=fopen(filename,"w");
				fclose(pFile);
				if(printFlag == 1)
				{
					cout<<myStoreList.size()<<endl;
				}
				for(itF=myStoreList.begin(); itF!=myStoreList.end(); itF++)
				{
					if(printFlag == 1)
					{
						cout<<"enters"<<endl;
					}
					storeListStr storeData = *itF;
					strcpy(filename,initMap["HomeDir"].asStr);
					strcat(filename,"/");
					strcat(filename,"kwrd_index");
					pFile=fopen(filename,"a");
					fprintf(pFile,"%d=",storeData.fileIndex);
					int j =0;
					for(j=0;j<256;j++)
					{
						fprintf(pFile,"%c",storeData.fileBitVector[j]);
					}
					fprintf(pFile,"\n");
					fclose(pFile);

					//Create the name_index file
					strcpy(filename,initMap["HomeDir"].asStr);
					strcat(filename,"/");
					strcat(filename,"name_index");
					pFile=fopen(filename,"a");
					fprintf(pFile,"%d=",storeData.fileIndex);
					fprintf(pFile,"%s",storeData.fileName);
					fprintf(pFile,"\n");
					fclose(pFile);

					//Create the nonce_index file
					strcpy(filename,initMap["HomeDir"].asStr);
					strcat(filename,"/");
					strcat(filename,"nonce_index");
					pFile=fopen(filename,"a");
					fprintf(pFile,"%d=",storeData.fileIndex);
					for(j=0;j<20;j++)
					{
						fprintf(pFile,"%02x",(uint8_t)storeData.fileNonce[j]);
					}
					fprintf(pFile,"\n");
					fclose(pFile);

					//Create the sha1_index file
					strcpy(filename,initMap["HomeDir"].asStr);
					strcat(filename,"/");
					strcat(filename,"sha1_index");
					pFile=fopen(filename,"a");
					fprintf(pFile,"%d=",storeData.fileIndex);
					for(j=0;j<20;j++)
					{
						fprintf(pFile,"%02x",(uint8_t)storeData.fileSha1[j]);
					}
					fprintf(pFile,"\n");
					fclose(pFile);	
				}
			}
			pthread_mutex_unlock(&storelock);
			pthread_mutex_unlock(&cacheSizeLock);
			if(printFlag == 1)
			{
				cout<<"Store file at the node"<<endl;
			}
			uoidStr tempUOIDStr;
			struct timeval time1;
			gettimeofday(&time1, NULL);
			tempUOIDStr.timeOfTheDay = (double)(time1.tv_sec * (1e+3) + (double)time1.tv_usec/1000);
			tempUOIDStr.msgLifeTime = initMap["MsgLifetime"].asDouble;
			memcpy(tempUOIDStr.msgUOID, msgRespHdr.uoid, 20);
			pthread_mutex_lock(&uoidlock);
			myUOIDList.push_back(tempUOIDStr);
			pthread_mutex_unlock(&uoidlock);
			int totalDataLength = msgRespHdr.datalength;
			int forCounter = totalDataLength/8192;
			int extraCounter = totalDataLength%8192;
			int f = 0;
			pthread_mutex_lock(&eventQueueMutex);
			listBuffStruct tempListBuff = eventQueue.front();
			eventQueue.pop_front();
			pthread_mutex_unlock(&eventQueueMutex);
			uint32_t metaLength;
			memcpy(&metaLength,&(tempListBuff.listBuff[0]),sizeof(uint32_t));
			FILE *pFile;
			pthread_mutex_lock(&fileCountsLock);
			int index = globalFileCounter;
			char fc[10];
			memset(fc,'\0',10);
			sprintf(fc,"%d",globalFileCounter++);
			pthread_mutex_unlock(&fileCountsLock);
			char storefilename[512];
			strcpy(storefilename,initMap["HomeDir"].asStr);
			strcat(storefilename,"/");
			strcat(storefilename,"files");
			mkdir(storefilename,S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
			strcat(storefilename,"/");
			strcat(storefilename,fc);
			strcat(storefilename,".meta");
			if(printFlag == 1)
			{
				cout<<storefilename<<endl;
			}
			pFile = fopen(storefilename,"w");
			if(pFile==NULL)
			{
				exit(0);
			}
			fwrite(&(tempListBuff.listBuff[4]),1,metaLength,pFile);
			fclose(pFile);
			strcpy(storefilename,initMap["HomeDir"].asStr);
			strcat(storefilename,"/");
			strcat(storefilename,"files");
			strcat(storefilename,"/");
			strcat(storefilename,fc);
			strcat(storefilename,".data");
			pFile = fopen(storefilename,"w");
			if(forCounter==0)
			{
				if(printFlag == 1)
				{
					cout<<totalDataLength<<endl;
				}
				fwrite(&(tempListBuff.listBuff[4+metaLength]),1,totalDataLength-4-metaLength,pFile);
			}
			else
			{
				fwrite(&(tempListBuff.listBuff[4+metaLength]),1,8192-4-metaLength,pFile);
				for(f=1;f<forCounter;f++)
				{
					pthread_mutex_lock(&eventQueueMutex);
					tempListBuff = eventQueue.front();
					eventQueue.pop_front();
					pthread_mutex_unlock(&eventQueueMutex);
					fwrite(tempListBuff.listBuff,1,8192,pFile);
				}
				if(extraCounter!=0)
				{
					pthread_mutex_lock(&eventQueueMutex);
					tempListBuff = eventQueue.front();
					eventQueue.pop_front();
					pthread_mutex_unlock(&eventQueueMutex);
					fwrite(tempListBuff.listBuff,1,extraCounter,pFile);
				}
			}
			fclose(pFile);
			strcpy(storefilename,initMap["HomeDir"].asStr);
			strcat(storefilename,"/");
			strcat(storefilename,"files");
			strcat(storefilename,"/");
			strcat(storefilename,fc);
			int noForwardingFlag = meta_file_parsing(storefilename,index);
			if(msgRespHdr.timetolive==1||noForwardingFlag == 1)
			{
				if(printFlag == 1)
				{
					cout<<"TTl or NoForwarding "<<noForwardingFlag<<" Problem"<<endl;
				}
				//no forwarding
			}
			else
			{
				if(printFlag == 1)
				{
					cout<<"Forwarding the file"<<endl;
				}
				pthread_mutex_lock(&writeMapMutex);
				listBuffStruct lbtemp;
				lbtemp.logtype=2;
				int tempTTL = msgRespHdr.timetolive;
				tempTTL--;
				msgRespHdr.timetolive = tempTTL;
				memset(&lbtemp.listBuff, '\0', 8192);
				fillMsgHdrBuffer(msgRespHdr, lbtemp.listBuff);
				struct stat st;
				int k = 0;
				char ch[8192];
				strcpy(storefilename,initMap["HomeDir"].asStr);
				strcat(storefilename,"/");
				strcat(storefilename,"files");
				strcat(storefilename,"/");
				strcat(storefilename,fc);
				strcat(storefilename,".meta");
				k = stat(storefilename, &st);
				uint32_t sz = st.st_size;
				for(it=writeMap.begin(); it!=writeMap.end(); it++)
				{
					if(drand48()>initMap["NeighborStoreProb"].asDouble||(*it).first==eventReq.portNumber)
					{
						continue;
					}
					if(printFlag == 1)
					{
						cout<<"sending now to "<<(*it).first<<endl;
					}
					memset(&lbtemp.listBuff, '\0', 8192);
					fillMsgHdrBuffer(msgRespHdr, lbtemp.listBuff);
					lbtemp.datalength = 27;
					lbtemp.portNumber = (*it).first;
					writeListData listDataStr = writeMap[(*it).first];
					listDataStr.response.push_back(lbtemp);
					int f = 0;
					FILE *pFile1;
					strcpy(storefilename,initMap["HomeDir"].asStr);
					strcat(storefilename,"/");
					strcat(storefilename,"files");
					strcat(storefilename,"/");
					strcat(storefilename,fc);
					strcat(storefilename,".meta");
					pFile1 = fopen(storefilename,"r");
					memset(ch,'\0',8192);
					fread(&ch,1,sz,pFile1);
					memset(lbtemp.listBuff, '\0', 8192);
					memcpy(&(lbtemp.listBuff[0]),&sz,sizeof(uint32_t));
					memcpy(&(lbtemp.listBuff[4]),&ch,sz);
					fclose(pFile1);
					strcpy(storefilename,initMap["HomeDir"].asStr);
					strcat(storefilename,"/");
					strcat(storefilename,"files");
					strcat(storefilename,"/");
					strcat(storefilename,fc);
					strcat(storefilename,".data");
					pFile1 = fopen(storefilename,"r");
					if(forCounter==0)
					{
						fread (&ch,1,totalDataLength-4-sz,pFile1);
						memcpy(&(lbtemp.listBuff[4+sz]),&ch,totalDataLength-4-sz);
						lbtemp.datalength = totalDataLength;
						listDataStr.response.push_back(lbtemp);
					}
					else
					{
						fread (&ch,1,8192-sz-sizeof(uint32_t),pFile1);
						memcpy(&(lbtemp.listBuff[4+sz]),&ch,8192-sz-sizeof(uint32_t));
						lbtemp.datalength = 8192;
						listDataStr.response.push_back(lbtemp);
						for(f=1;f<forCounter;f++)
						{
							fread (&ch,1,8192,pFile1);
							memset(lbtemp.listBuff,'\0',8192);
							lbtemp.datalength = 8192;
							memcpy(&(lbtemp.listBuff[0]),&ch,8192);
							listDataStr.response.push_back(lbtemp);
						}
						if(extraCounter!=0)
						{
							fread (&ch,1,extraCounter,pFile1);
							memset(lbtemp.listBuff,'\0',8192);
							lbtemp.datalength = extraCounter;
							memcpy(&(lbtemp.listBuff[0]),&ch,extraCounter);
							listDataStr.response.push_back(lbtemp);
						}
					}
					fclose(pFile1);
					writeMap[(*it).first] = listDataStr;
				}
				pthread_mutex_unlock(&writeMapMutex);
			
			}
		}
	}
}
